﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace PlanetViewer
{
    public class Controls
    {
        public KeyboardState KeyboardState;
        private KeyboardState _pKeyboardState;

        private int _vWidth;
        private int _vHeight;

        private float _scrolltarget = 0f;

        private float _scrollrate = 40f;
        private float _scrolldist = 0.035f;
        private float _scrolldead = 0.002f;

        public int _Mode = 0;

        public MouseState MouseState;
        private int _pMouseX = 0;
        private int _pMouseY = 0;

        public float _mouseAng = 0f;
        public int MouseSpine = 0;
        public int _pMouseSpine = 0;

        private float _scroll;
        private float _pScroll;
        public float Scroll
        {
            get { return _scroll; }
            set
            {
                _pScroll = _scroll;
                _scroll = value;
            }
        }

        //---------------------------------------------------------------------------------//
        #region Camera

        public Camera _Cam;

        private float _viewAngle = 0f;
        public float _pAngle = 0f;
        public float Angle
        {
            get { return _viewAngle; }
            set
            {
                if (value != _viewAngle)
                {
                    _viewAngle = value;

                    float lim = 2 * MathHelper.Pi;

                    if (_viewAngle < 0) _viewAngle += lim;
                    if (_viewAngle >= lim) _viewAngle -= lim;

                    _Cam.RefreshRot(_viewAngle);
                }
            }
        }

        private float _viewZoom = 1.0f;
        public float _pZoom = 1.0f;
        private float _zMax = 1.0f;
        private float _zMin = 0.08f;

        private float _zCamXLock = 0.7f;        
        private float _zTDMmargin = 0.097f;

        private float _zTDMk;

        public float TDMLength;

        public float Zoom
        {
            get { return _viewZoom; }
            set
            {
                _viewZoom = value;
                if (_viewZoom < _zMin) _viewZoom = _zMin;
                if (_viewZoom > _zMax) _viewZoom = _zMax;

                float XTar;
                if (_viewZoom < _zCamXLock)
                {
                    float xd1 = _viewZoom - _zCamXLock;
                    float xd2 = _zMin - _zCamXLock;

                    XTar = this._activePlanet.Radius * (1 - (xd1 * xd1 * xd1 * xd1 * xd1 * xd1) / (xd2 * xd2 * xd2 * xd2 * xd2 * xd2));
                }
                else
                {
                    XTar = this._activePlanet.Radius;
                }

                if (XTar < 0) XTar = 0;

                _Cam.RefreshPos(new Vector2(XTar, 0f), _viewZoom);

                TDMLength = this._activePlanet.Terrain.DrawMask._minMLengthF + _zTDMk * ((1 / (float)Math.Sin(_viewZoom * MathHelper.PiOver2)) - 1);
                if (TDMLength > 1.0f) TDMLength = 1.0f;

                _activePlanet.Terrain.DrawMask.CalculateMaskLength(TDMLength);
            }
        }

        #endregion
        //---------------------------------------------------------------------------------//

        private PlanetViewer _planetViewer;

        private Planet _activePlanet { get { return this._planetViewer.ActivePlanet; } }

        public Controls(PlanetViewer plViewer, int viewWidth, int viewHeight, Camera Cam)
        {
            _planetViewer = plViewer;

            _vWidth = viewWidth;
            _vHeight = viewHeight;

            _Cam = Cam;
            _zMax = 0.026f * _Cam._bounds;
            Zoom = _zMax;

            _scrolldead *= 400f / (float)_activePlanet.Radius;
            _scrolldist *= 400f / (float)_activePlanet.Radius;

            _zTDMk = (1 - _activePlanet.Terrain.DrawMask._minMLengthF) / ((1 / (float)Math.Sin(_zTDMmargin * MathHelper.PiOver2)) - 1);
        }

        public string GetModeName()
        {
            switch (_Mode)
            {
                case 1:
                    { return "Structure Placement"; }
                case 2:
                    { return "Terrain Editing"; }
                default:
                    { return "Default"; }
            }
        }

        public void Update()
        {
            _pAngle = Angle;
            _pZoom = Zoom;

            #region KeyboardCalcs

            KeyboardState = Keyboard.GetState();

            if (KeyboardState.IsKeyDown(Keys.Add)) Zoom *= 1.01f;

            if (KeyboardState.IsKeyDown(Keys.Subtract)) Zoom *= 0.99f;

            if (KeyboardState.IsKeyDown(Keys.Enter)) Zoom = _zMax;

            //if (KeyboardState.IsKeyDown(Keys.Multiply) & !_pKeyboardState.IsKeyDown(Keys.Multiply)) this._activePlanet.Terrain.LOD += 1;

            //if (KeyboardState.IsKeyDown(Keys.Divide) & !_pKeyboardState.IsKeyDown(Keys.Divide)) this._activePlanet.Terrain.LOD -= 1;

            if (KeyboardState.IsKeyDown(Keys.Space) & !_pKeyboardState.IsKeyDown(Keys.Space))
            {
                _Mode++;
                if (_Mode > 2) _Mode = 0;
            }

            _pKeyboardState = KeyboardState;

            #endregion

            #region MouseCalcs

            MouseState = Mouse.GetState();
            Scroll = MouseState.ScrollWheelValue;

            if (_scroll != _pScroll)
            {
                float delta = _scroll - _pScroll;

                if (delta * _scrolltarget <= 0)
                {
                    _scrolltarget = Math.Sign(delta) * _scrolldist;
                }
                else
                {
                    _scrolltarget += Math.Sign(delta) * _scrolldist;
                }
            }

            if (_scrolltarget != 0)
            {
                float delta = _scrolltarget / _scrollrate;

                Angle -= delta;

                if (_scrolltarget < -1 * _scrolldead | _scrolltarget > _scrolldead)
                {
                    _scrolltarget -= delta;
                }
                else
                {
                    _scrolltarget = 0;
                }
            }

            _pMouseSpine = MouseSpine;

            if (MouseState.X != _pMouseX | MouseState.Y != _pMouseY)
            {
                _pMouseX = MouseState.X;
                _pMouseY = MouseState.Y;

                Vector2 M1 = new Vector2();
                M1.X = MouseState.X - (_vWidth / 2);
                M1.Y = (_vHeight / 2) - MouseState.Y + 10.85f * Zoom * _Cam.Target.X;

                _mouseAng = (float)Math.Atan2(M1.X, M1.Y);

                MouseSpine = GetMouseSpine(_mouseAng);
            }
            else if (Angle != _pAngle)
            {
                MouseSpine = GetMouseSpine(_mouseAng);
            }

            if (MouseState.LeftButton == ButtonState.Pressed)
            {
                if (_Mode == 2)
                {
                    this._planetViewer.ActivePlanet.Terrain.AddEdgeHeight(MouseSpine, 0.01f);

                    this._planetViewer.ActivePlanet.Terrain.DrawMask.AdjustVertices(true);
                }
            }
            
            if (MouseState.RightButton == ButtonState.Pressed)
            {
                if (_Mode == 2)
                {
                    this._planetViewer.ActivePlanet.Terrain.AddEdgeHeight(MouseSpine, -0.01f);

                    this._planetViewer.ActivePlanet.Terrain.DrawMask.AdjustVertices(true);
                }
            }    

            #endregion
        }

        private int GetMouseSpine(float mouseAngle)
        {
            float totalMouseAng = (Angle + mouseAngle);
            float Pi2 = 2 * MathHelper.Pi;
            if (totalMouseAng < 0) totalMouseAng += Pi2; else if (totalMouseAng > Pi2) totalMouseAng -= Pi2;

            int spine = (int)Math.Floor(totalMouseAng / this._activePlanet.SpineAngle);

            return spine;
        }
    }
}